import * as fs from 'fs';
import { type } from 'os';

/**
 * 单个文件的下载器
 */
export class FileDownloader {

    /**
     * 下载文件基类
     * @param srcName url路径
     * @param sourceName 下载到本地的路径
     */
    constructor(srcName, sourceName) {
        this.srcName = srcName;
        this.sourceName = sourceName;
        this.suspend = false;
        //上一次返回进度的时间节点
        this.preTime = 0;
        this.totalSize = 0;
        this.progressInteval = null;
        this.request = null;
        this.outStream = null;
        this.pauseDownload = this.pauseDownload.bind(this)
    }

    initFileDownloader () {
        this.suspend = false;
        this.progressInteval = null;
        this.request = null;
        this.outStream = null;
    }

    /**
     * 异步下载单个文件
     * @param fileSizeCallback  文件大小回调  回调值 -- totalBytes
     * @param progressCallback  进度回调  回调值 -- (receiveBytes, nowSpeed)
     * @param successCallback  下载成功回调  回调值 -- 无
     * @param errorCallback  下载失败回调  回调值 -- 无
     */
    downloadFile (fileSizeCallback, progressCallback, successCallback, errorCallback) {
        const request = require('request');
        let _this = this;
        this.preTime = new Date().getTime();
        this.request = request({
            method: 'GET',
            uri: _this.srcName,
            strictSSL: false,
            timeout: 300000,
        });
        // console.log('构造文件路径');
        //构造文件路径
        let fileName = _this.sourceName;
        // return
        //check需要下载后，该方法需先删除文件再下载
        if (fs.existsSync(fileName)) {
            fs.unlinkSync(fileName);
        }
        this.outStream = fs.createWriteStream(fileName);
        try {
            // if (fileName.indexOf('BosVideo') != -1  && fileName.indexOf('.zip') != -1) {
            //     console.log('下载新视频压缩包暂时不写入文件流',this.outStream);
            // }else{
                this.pipe = this.request.pipe(this.outStream);
            // }
        } catch (error) {
            console.log(error)
        }

        let totalBytes = 0;
        let receivedBytes = 0;
        let nowReceivedBytes = 0;
        //检测输出流被关闭后在去销毁输出流
        this.outStream.on('close', () => {
            this.outStream.destroy()
            this.outStream = null
            console.log('检测输出流被关闭')
        })
        //捕获输出流测错误
        this.outStream.on('error', e => {
            console.error(e)
            errorCallback();
            this.pauseDownload()
        })
        this.request.on('response', (data) => {
            if (!_this.suspend) {
                // 更新总文件字节大小
                totalBytes = parseInt(data.headers['content-length'], 10);
                totalBytes = (totalBytes / (1024 * 1024)).toFixed(2);
                if (fileSizeCallback) {
                    fileSizeCallback(totalBytes);
                }
                _this.progressInteval = setInterval(function () {
                    if (progressCallback) {
                        let speed = (nowReceivedBytes / 1024).toFixed(1);
                        progressCallback(receivedBytes, speed);
                        //重置当前已经收到的字节数
                        nowReceivedBytes = 0;
                    }
                }, 1000);
            }
        });
        this.request.on('data', (chunk) => {
            if (!_this.suspend) {
                // 更新下载的文件块字节大小
                receivedBytes += chunk.length;
                nowReceivedBytes += chunk.length;
            }
        });

        this.request.on('end', () => {
            if (_this.progressInteval) {
                clearInterval(_this.progressInteval);
            }
            if (!_this.suspend) {//如果不是暂停,成功回调
                if (successCallback) {
                    successCallback()
                }
            }
        });

        this.request.on('error', (e) => {
            if (e.message === 'ESOCKETTIMEDOUT') {
                console.log('下载触发超时',e)
                // if (_this.progressInteval) {
                //     clearInterval(_this.progressInteval);
                // }
                // try {
                //     _this.request.destroy();
                // } catch (e) {
                //     console.log('timeout停止下载过程中出错！');
                // }
                // try {
                //     _this.outStream.destroy();
                // } catch (e) {
                //     console.log('timeout销毁文件流出错！');
                // }
                // if (!_this.suspend) {
                //     if (errorCallback) {
                //         errorCallback();
                //     }
                // }
            }
            console.log('下载过程中遇到错误！');
            if (_this.progressInteval) {
                clearInterval(_this.progressInteval);
            }
            try {
                if (_this.request) {
                    _this.request.destroy();
                }
            } catch (e) {
                console.log('error停止下载过程中出错！');
            }
            try {
                if (_this.outStream) {
                    _this.outStream.destroy();
                }
            } catch (e) {
                console.log('error销毁文件流出错！');
            }
            if (!_this.suspend) {
                if (errorCallback) {
                    errorCallback();
                }
            }
        });

        this.request.on('timeout', () => {
            console.log('下载过程超时！');
            if (_this.progressInteval) {
                clearInterval(_this.progressInteval);
            }
            try {
                _this.request.destroy();
            } catch (e) {
                console.log('timeout停止下载过程中出错！');
            }
            try {
                _this.outStream.destroy();
            } catch (e) {
                console.log('timeout销毁文件流出错！');
            }
            if (!_this.suspend) {
                if (errorCallback) {
                    errorCallback();
                }
            }
        });
    }

    /**
     * 暂停下载
     */
    pauseDownload () {
        if (this.progressInteval) {
            clearInterval(this.progressInteval);
        }
        this.suspend = true;
        if (this.outStream) {
            this.outStream.close();
        }
        if (this.request) {
            this.request.destroy();
            this.request = null;
        }
    }

    /**
     * 是否需要下载（已经存在的，且文件完整的话，不需要下载）
     * @param callback
     */
    checkDownload (callback) {
        console.log(this.sourceName)
        let _this = this;
        if (fs.existsSync(_this.sourceName)) {
            fs.stat(_this.sourceName, function (err, stats) {
                // console.log("sourceName size --> " + stats.size + " remote size --> " + _this.totalSize)
                if (stats.size === _this.totalSize) {
                    if (callback) {
                        callback(false, stats.size);
                    }
                } else {
                    if (callback) {
                        callback(true);
                    }
                }
            })
        } else {
            if (callback) {
                callback(true);
            }
        }
    }

    /**
     * 同步获取文件的大小，返回byte
     * @returns {int 字节大小}
     */
    static getTotalSize (url, successCallback) {
        console.log('同步获取文件的大小，返回byte',url);
        //判断url是否为空,为空则直接返回
        if (!url) {
            successCallback(0);
            return
        }
        const http = require('http');
        const https = require('https');
        if (url.indexOf("http://") > -1) {
            http.get(url, res => {
                console.log('http',res);
                const { statusCode } = res;
                if (statusCode !== 200) {
                    if (statusCode === 302) {
                        let newUrl = res.headers.location;
                        console.log("getTotalSize 触发了 重定向...  newUrl => " + newUrl);
                        FileDownloader.getTotalSize(newUrl, successCallback);
                        // 消费响应的数据来释放内存。
                        res.resume();
                        return;
                    } else {
                        if (successCallback) {
                            successCallback(0);
                        }
                        // 消费响应的数据来释放内存。
                        res.resume();
                        return;
                    }
                }
                let contentSize = res.headers['content-length'];
                contentSize = parseInt(contentSize, 10);
                if (successCallback) {
                    successCallback(contentSize);
                }
                // 消费响应的数据来释放内存。
                res.resume();
            }).on('error', (e) => {
                console.error(`获取文件头出现错误: ${e.message}`);
                if (successCallback) {
                    successCallback(0);
                }
            })
        } else if (url.indexOf("https://") > -1) {
            https.get(url, res => {
                console.log('https'+res);
                const { statusCode } = res;
                if (statusCode !== 200) {
                    if (successCallback) {
                        successCallback(0);
                    }
                    // 消费响应的数据来释放内存。
                    res.resume();
                    return;
                }
                let contentSize = res.headers['content-length'];
                contentSize = parseInt(contentSize, 10);
                if (successCallback) {
                    successCallback(contentSize);
                }
                // 消费响应的数据来释放内存。
                res.resume();
            }).on('error', (e) => {
                console.error(`获取文件头出现错误: ${e.message}`);
                if (successCallback) {
                    successCallback(0);
                }
            })
        } else {
            if (successCallback) {
                successCallback(0);
            }
        }
    }

    /**
     * 同步下载单个文件（UTF-8字节流）
     * @param 文件url
     * @returns 返回文件的文本值
     */
    static async syncQueryOneFileContent (url) {
        console.log('url------>'+url);
        const https = require('https');

        let downPromise = function () {
            return new Promise((resolve, reject) => {
                https.get(url, res => {
                    const { statusCode } = res;
                    if (statusCode !== 200) {
                        console.error("statusCode is not 200");
                        reject("error");
                        // 消费响应的数据来释放内存。
                        res.resume();
                        return;
                    }
                    res.setEncoding('utf8');
                    let rawData = '';
                    res.on('data', (chunk) => {
                        rawData += chunk;
                    });
                    res.on('end', () => {
                        resolve(rawData);
                    });
                }).on('error', (e) => {
                    console.error(`同步获取云文件内容出现错误: ${e.message}`);
                    reject("error");
                }).on('timeout', (e) => {
                    console.error(`同步获取云文件内容超时: ${e.message}`);
                    reject("error");
                }).on('abort', (e) => {
                    console.error(`同步获取云文件内容取消: ${e.message}`);
                    reject("error");
                });
            });
        }

        let fileContent = await downPromise();
        return fileContent;
    }

    /**
     * 同步下载单个文件，并保存
     * @param url 文件的url地址
     * @param fileName 本地保存的文件名称
     * @param isCheck 是否需要check
     * @returns {Promise<void>}
     */
    static syncDownloadOneFile (url, fileName, isCheck, successCallback, errorCallback) {
        console.log('fileName=>>>>', fileName)
        if (isCheck) {
            if (fs.existsSync(fileName)) {
                FileDownloader.getTotalSize(url, function (resSize) {
                    fs.stat(fileName, function (err, stats) {
                        if (err) {
                            if (errorCallback) {
                                errorCallback();
                            }
                            return
                        }
                        if (stats.size && (stats.size === resSize)) {
                            if (successCallback) {
                                successCallback();
                            }
                        } else {
                            const request = require('request');
                            let writeStream = fs.createWriteStream(fileName);
                            request
                                .get(url)
                                .on('error', function (err) {
                                    if (errorCallback) {
                                        errorCallback();
                                    }
                                })
                                .pipe(writeStream)

                            writeStream.on('finish', function () {
                                if (successCallback) {
                                    successCallback();
                                }
                            }).on('error', function (err) {
                                if (errorCallback) {
                                    errorCallback();
                                }
                            })
                        }
                    })
                });
            } else {
                const request = require('request');
                let writeStream = fs.createWriteStream(fileName);
                request
                    .get(url)
                    .on('error', function (err) {
                        if (errorCallback) {
                            errorCallback();
                        }
                    })
                    .pipe(writeStream)

                writeStream.on('finish', function () {
                    if (successCallback) {
                        successCallback();
                    }
                }).on('error', function (err) {
                    if (errorCallback) {
                        errorCallback();
                    }
                })
            }
        } else {
            const request = require('request');
            if (fs.existsSync(fileName)) {
                fs.unlinkSync(fileName);
            }
            let writeStream = fs.createWriteStream(fileName);
            request
                .get(url)
                .on('error', function (err) {
                    if (errorCallback) {
                        errorCallback();
                    }
                })
                .pipe(writeStream)

            writeStream.on('finish', function () {
                if (successCallback) {
                    successCallback();
                }
            }).on('error', function (err) {
                if (errorCallback) {
                    errorCallback();
                }
                if (writeStream) {
                    writeStream.destroy();
                }
            })
        }
    }
}
